﻿//+-------------------------------------------------------------------------------------------------------------------------------------------------------
//
//Description: VmRoleResourceExtensionViewModel
//Author: Aravind Ramarathinam (aravr@microsoft.com)
//
//Copyright © Microsoft Corporation
//Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
//You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 
//
//THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, 
//INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. 
//See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
// 
//--------------------------------------------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Windows;
using VmroleAuthor.Helpers;
using VmroleAuthor.Models;

namespace VmroleAuthor.ViewModels
{
    public class VmRoleResourceExtensionViewModel : TreeViewItemViewModel, IDataErrorInfo, IProduceValidationResults, ICanBeRemovedFromTree, ISaveToDisk, IJsonOutput
    {
        private readonly VMRoleResourceExtensionDefinition resourceExtensionDefinition;
        private readonly ApplicationProfileViewModel applicationProfileViewModel;
        private readonly ResourceExtensionDataPackageViewModel resourceExtensionDataPackageViewModel;
        private readonly ParametersViewModel resourceExtensionParametersViewModel;
        private readonly ResourceExtensionRequirementsViewModel resourceExtensionRequirementsViewModel;

        public const string DefaultDataPackageFolderName = "DataPackage";

        private static class PropertyNames
        {
            public const string Identity = "Identity";
            public const string Name = "Name";
            public const string Version = "Version";
            public const string Publisher = "Publisher";
            public const string SchemaVersion = "SchemaVersion";
            public const string Type = "Type";  
        }

        public VmRoleResourceExtensionViewModel(VMRoleResourceExtensionDefinition resExtDefinition, ResourceExtensionStorageInfo resourceExtensionStorageInfo)
            : base(null, true)
        {
            this.resourceExtensionDefinition = resExtDefinition;
            this.ResourceExtensionStorageInfo = resourceExtensionStorageInfo;
            if (this.resourceExtensionDefinition.ExtensionSettings == null)
            {
                this.resourceExtensionDefinition.ExtensionSettings = new ExtensionSettings();
            }
            this.resourceExtensionDataPackageViewModel = new ResourceExtensionDataPackageViewModel(resourceExtensionDefinition.DataPackage, this);
            this.applicationProfileViewModel = new ApplicationProfileViewModel(this.resourceExtensionDefinition.ExtensionSettings, this);
            this.resourceExtensionParametersViewModel = new ParametersViewModel(this.resourceExtensionDefinition.ResourceExtensionParameters, this);
            this.resourceExtensionRequirementsViewModel = new ResourceExtensionRequirementsViewModel(this);
            this.ValidationResults = new ObservableCollection<CloudServiceModelValidationResult>();
        }

        public ResourceExtensionStorageInfo ResourceExtensionStorageInfo { get; private set; }

        public  VMRoleResourceExtensionDefinition ResourceExtensionDefinition
        {
            get { return this.resourceExtensionDefinition; }
        }

        public string Identity
        {
            get
            {
                return "Resource Extension ( " + this.resourceExtensionDefinition.Name + ":" + this.resourceExtensionDefinition.Version + " : " + 
                       this.resourceExtensionDefinition.Publisher + " )";
            }
        }

        public string Name
        {
            get { return this.resourceExtensionDefinition.Name; }
            set
            {
                this.resourceExtensionDefinition.Name = value;
                this.OnPropertyChanged(PropertyNames.Name);
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string Version
        {
            get { return this.resourceExtensionDefinition.Version; }
            set
            {
                this.resourceExtensionDefinition.Version = value;
                this.OnPropertyChanged((PropertyNames.Version));
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string Publisher
        {
            get { return this.resourceExtensionDefinition.Publisher; }
            set 
            { 
                this.resourceExtensionDefinition.Publisher = value;
                this.OnPropertyChanged(PropertyNames.Publisher);
                this.OnPropertyChanged(PropertyNames.Identity);
            }
        }

        public string SchemaVersion
        {
            get { return this.resourceExtensionDefinition.SchemaVersion; }
            set 
            { 
                this.resourceExtensionDefinition.SchemaVersion = value;
                this.OnPropertyChanged(PropertyNames.SchemaVersion);
            }
        }

        public string Type
        {
            get { return this.resourceExtensionDefinition.Type; }
            set
            {
                this.resourceExtensionDefinition.Type = value;
                this.OnPropertyChanged(PropertyNames.Type);
            }
        }      
       
        public ApplicationProfileViewModel ApplicationProvileViewModel
        {
            get { return this.applicationProfileViewModel; }
        }

        public ParametersViewModel ResourceExtensionParametersViewModel
        {
            get { return this.resourceExtensionParametersViewModel; }
        }

        public ResourceExtensionDataPackageViewModel ResourceExtensionDataPackageViewModel
        {
            get { return this.resourceExtensionDataPackageViewModel; }
        }


        public ResourceExtensionRequirementsViewModel ResourceExtensionRequirementsViewModel
        {
            get { return this.resourceExtensionRequirementsViewModel; }
        }

        public string PrettyJson
        {
            get { return this.GetPrettyJson(false); }
        }

        public string GetRawJson(bool popOnError)
        {
            try
                {
                    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(VMRoleResourceExtensionDefinition));                
                    using (MemoryStream ms = new MemoryStream())
                    {
                        serializer.WriteObject(ms, this.ResourceExtensionDefinition);                   
                        return Encoding.Default.GetString(ms.ToArray());
                    }
                }
                catch (Exception ex)
                {
                    string error = string.Format(
                        "Unable to serialize {0}. Click Validate. Exception details : {1}",
                        this.Name ?? "Unspecified name",
                        ex.Message);
                    if (popOnError)
                    {
                        MessageBox.Show(error, DefinitionViewModel.ToolHeader);
                    }
                    return error;
                }       
        }


        public string GetPrettyJson(bool popOnError)
        {
            return JsonHelper.PrettyPrentJson(this.GetRawJson(popOnError));          
        }

        public void SaveToDisk()
        {
            if (this.ResourceExtensionStorageInfo != null)
            {
                try
                {
                    using (StreamWriter outfile = new StreamWriter
                       (new FileStream(this.ResourceExtensionStorageInfo.JsonFileName, FileMode.Create, FileAccess.ReadWrite),
                       Encoding.UTF8))
                    {
                        outfile.Write(this.GetPrettyJson(true));
                    }                 
                    PackagingFormatHelper.CreatePackage(
                        this.ResourceExtensionStorageInfo.WorkingDir,
                        this.ResourceExtensionStorageInfo.FileToSavePath);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(
                        string.Format(
                        "Unable to save {0} json in package. File save path {1}. Exception details : {2}",
                        this.Name ?? "Unspecified name",
                        this.ResourceExtensionStorageInfo.JsonFileName,
                        ex.Message), "Save VmRole Resource Extension");
                }
            }
            else
            {
                MessageBox.Show(
                       string.Format(
                       "Unable to save {0} json in package. Storage Info not set",
                       this.Name ?? "Unspecified name"), "Save VmRole Resource Extension");               
            }     
        }

        protected override void LoadChildren()
        {
           this.Children.Add(this.resourceExtensionRequirementsViewModel);
           this.Children.Add(this.resourceExtensionDataPackageViewModel);
           this.Children.Add(this.resourceExtensionParametersViewModel);
           this.Children.Add(this.applicationProfileViewModel);          
        }

        public void Cleanup()
        {
            //By design. The currently saved resext is not impacted.
        }
      
        public string this[string propertyName]
        {
            get
            {
                string error = null;
                switch (propertyName)
                {
                    case PropertyNames.Name:
                        error = ParameterizationHelper.ValidateCannotBeParameterizedBySpecification(this.Name);
                        break;
                    case PropertyNames.Version:
                        error = ValidationHelper.ValidateVersionFormat(this.Version);                       
                        break;   
                   case PropertyNames.Publisher:
                        error = ParameterizationHelper.ValidateCannotBeParameterizedBySpecification(this.Publisher);
                        break;
                   case PropertyNames.SchemaVersion:
                        error = ValidationHelper.ValidateVersionFormat(this.SchemaVersion, false);  
                        if (error == null)
                        {
                            if (!VMRoleResourceExtensionDefinition.ExpectedSchemaVersion.Equals(this.SchemaVersion, StringComparison.Ordinal))
                            {
                                error = "Supported: " + VMRoleResourceExtensionDefinition.ExpectedSchemaVersion;
                            }
                        }
                        break;                    
                    case PropertyNames.Identity:
                        //try
                        //{
                        //    DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(VMRoleResourceExtensionDefinition));
                        //    using (MemoryStream ms = new MemoryStream())
                        //    {
                        //        ser.WriteObject(ms, this.ResourceExtensionDefinition);
                        //    }                          
                        //}
                        //catch (Exception ex)
                        //{
                        //    error = string.Format("Unable to serialize json {0}. Exception details : {1}",
                        //        this.Name ?? "Unspecified name", ex.Message);                            
                        //}
                        break;
                }
                return error;
            }
        }

        public string Error { get; set; }

        public ObservableCollection<CloudServiceModelValidationResult> ValidationResults { get; private set; }

        public void UpdateValidationResults()
        {
            this.IsExpanded = true;
            ModelValidationResultsHelper.UpdateValidationResults(this, typeof(PropertyNames));
            //Add any other cross-field validations error           
        }    

        public static VmRoleResourceExtensionViewModel CreateNewResourceExtension(ResourceExtensionStorageInfo resourceExtensionStorageInfo, bool isWindowsResExt = true)
        {
            VMRoleResourceExtensionDefinition resExtDef = new VMRoleResourceExtensionDefinition();
            resExtDef.Name = Path.GetFileNameWithoutExtension(resourceExtensionStorageInfo.JsonFileName);
            resExtDef.Publisher = "UnspecifiedPublisher";
            resExtDef.Version = "1.0.0.0";
            resExtDef.SchemaVersion = VMRoleResourceExtensionDefinition.ExpectedSchemaVersion;
            resExtDef.SchemaVersion = VMRoleResourceExtensionDefinition.ExpectedSchemaVersion;
            resExtDef.Type = VMRoleResourceExtensionDefinition.ExpectedType;
            resExtDef.ExtensionSettings = new ExtensionSettings();        
            resExtDef.ExtensionSettings.ApplicationProfile = new ApplicationProfile();
            resExtDef.ExtensionSettings.ApplicationProfile.Name = resExtDef.Name;
            resExtDef.ExtensionSettings.ApplicationProfile.ApplicationPayload = new List<ApplicationPayload>();   
            resExtDef.ResourceRequirements = new ResourceRequirements();
            resExtDef.ResourceRequirements.OSVirtualHardDiskRequirements = new List<string>();
            if (isWindowsResExt)
            {
                resExtDef.ExtensionSettings.ApplicationProfile.WindowsApplicationProfile = new WindowsApplicationProfile();
                resExtDef.ResourceRequirements.OSVirtualHardDiskRequirements.Add(RecommendedTags.GeneralTags[0]);                
            }
            else
            {
                resExtDef.ExtensionSettings.ApplicationProfile.LinuxApplicationProfile = new LinuxApplicationProfile();
                resExtDef.ResourceRequirements.OSVirtualHardDiskRequirements.Add(RecommendedTags.GeneralTags[1]);
            }
            
            VmRoleResourceExtensionViewModel resext = new VmRoleResourceExtensionViewModel(resExtDef, resourceExtensionStorageInfo);
            return resext;
        }       
    }
}
